package com.jiadakeji.ppm.common.utils;


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.jiadakeji.ppm.modules.weixin.entity.WechatUnifiedorderNotifyRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.util.Map;
import java.util.TreeMap;

/**
 * xmlutils
 * xml与对象相互转换
 *
 * @Author xuelongjiang
 */
public class XmlUtils {

    private static Logger logger = LoggerFactory.getLogger(XmlUtils.class);
    private static ObjectMapper xmlMapper;

    /**
     * 对象转换为xml
     * @param object
     * @return
     */
    public static  String objectToXml(Object object){

        StringWriter sw = new StringWriter();
        try {

            JAXBContext context = JAXBContext.newInstance(object.getClass());
            Marshaller marshaller =  context.createMarshaller();
            marshaller.marshal(object,sw);

        }catch (Exception e){
            e.printStackTrace();
            logger.error("对象解析xml出现异常，对象为"+object.toString());
        }

        return sw.toString();
    }

    public static  String objectToXml(Object object,String encoding){

        StringWriter sw = new StringWriter();
        try {

            JAXBContext context = JAXBContext.newInstance(object.getClass());
            Marshaller marshaller =  context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
            marshaller.marshal(object,sw);

        }catch (Exception e){
            e.printStackTrace();
            logger.error("对象解析xml出现异常，对象为"+object.toString());
        }

        return sw.toString();
    }

    /**
     * xml解析为对象
     * @param clazz
     * @param xml
     * @return
     */
    public static Object xmlToObject(Class clazz,String xml){

        Object xmlOject = null;
        try {

            JAXBContext context  = JAXBContext.newInstance(clazz);
            Unmarshaller unmarshaller = context.createUnmarshaller();
            StringReader reader = new StringReader(xml);
            xmlOject = unmarshaller.unmarshal(reader);

        }catch (Exception e){
            logger.error(e.toString());
            logger.error("xml转化为对象出现异常xml为:"+xml);

        }

        return xmlOject;
    }

    /**
     * XML格式字符串转换为Map
     *
     * @param strXML XML字符串
     * @return XML数据转换后的Map
     * @throws Exception
     */
    public static Map<String, String> xmlToMap(String strXML) throws Exception {
        try {
            Map<String, String> data = new TreeMap<String, String>();
//            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
//            documentBuilderFactory.setExpandEntityReferences(false);
//            documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING,true);
//            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            DocumentBuilder documentBuilder = newDocumentBuilder();
            InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
            org.w3c.dom.Document doc = documentBuilder.parse(stream);
            doc.getDocumentElement().normalize();
            NodeList nodeList = doc.getDocumentElement().getChildNodes();
            for (int idx = 0; idx < nodeList.getLength(); ++idx) {
                Node node = nodeList.item(idx);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                    data.put(element.getNodeName(), element.getTextContent());
                }
            }
            try {
                stream.close();
            } catch (Exception ex) {
                // do nothing
            }
            return data;
        } catch (Exception ex) {
            logger.warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
            throw ex;
        }

    }

    /**
     * 将Map转换为XML格式的字符串
     *
     * @param data Map类型数据
     * @return XML格式的字符串
     * @throws Exception
     */
    public static String mapToXml(Map<String, String> data) throws Exception {
//        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
//        documentBuilderFactory.setExpandEntityReferences(false);
//        documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING,true);
//        DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder();
        org.w3c.dom.Document document = newDocument();
        org.w3c.dom.Element root = document.createElement("xml");
        document.appendChild(root);
        for (String key: data.keySet()) {
            String value = data.get(key);
            if (value == null) {
                value = "";
            }
            value = value.trim();
            org.w3c.dom.Element filed = document.createElement(key);
            filed.appendChild(document.createTextNode(value));
            root.appendChild(filed);
        }
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        DOMSource source = new DOMSource(document);
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        StringWriter writer = new StringWriter();
        StreamResult result = new StreamResult(writer);
        transformer.transform(source, result);
        String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
        try {
            writer.close();
        }
        catch (Exception ex) {
        }
        return output;
    }


    /**
     * xml文件转换成对象
     * @param clazz
     * @param xmlPath
     * @return
     */
    public static Object convertXmlFileToObject(Class clazz, String xmlPath) {
        Object xmlObject = null;
        try {
            JAXBContext context = JAXBContext.newInstance(clazz);
            Unmarshaller unmarshaller = context.createUnmarshaller();
//            FileReader fr = null;
            InputStreamReader isr= null;
            try {
                isr = new InputStreamReader(new FileInputStream(xmlPath),"GB2312");
//                fr = new FileReader(xmlPath);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            xmlObject = unmarshaller.unmarshal(isr);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
        return xmlObject;
    }

    public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
        documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
        documentBuilderFactory.setXIncludeAware(false);
        documentBuilderFactory.setExpandEntityReferences(false);

        return documentBuilderFactory.newDocumentBuilder();
    }

    public static Document newDocument() throws ParserConfigurationException {
        return newDocumentBuilder().newDocument();
    }

    public static ObjectMapper getInstance(){
        if(xmlMapper == null){
            xmlMapper = new XmlMapper();
        }
        return xmlMapper;
    }

    public static Object jacksonXmlToObj( String xml,Class clazz) throws Exception{
        return getInstance().readValue(xml,clazz);
    }

    public static String jacksonObjToXml(Object object){
        try {
            String xml =   getInstance().writeValueAsString(object);
            logger.info(xml);
            return xml ;
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }


    public static void main(String[] args){

       /* TaxPayerInfo taxPayerInfo2 = (TaxPayerInfo) convertXmlFileToObject(TaxPayerInfo.class,"/Users/longjiangxue/git/ppm/src/main/java/com/jiadakeji/ppm/modules/invoice/entity/客户编码180301_123405.xml");

        List<PayerInfo> list = taxPayerInfo2.getPayerInfos();
        System.out.println(list);*/

        String xml = "<xml>\n" +
                "  <appid><![CDATA[wxed8e514891b578b9]]></appid>\n" +
                "  <bank_type><![CDATA[CCB_CREDIT]]></bank_type>\n" +
                "  <cash_fee><![CDATA[2035]]></cash_fee>\n" +
                "  <coupon_count><![CDATA[1]]></coupon_count>\n" +
                "  <coupon_fee>15</coupon_fee>\n" +
                "  <coupon_fee_0><![CDATA[15]]></coupon_fee_0>\n" +
                "  <coupon_id_0><![CDATA[2000000036925069074]]></coupon_id_0>\n" +
                "  <fee_type><![CDATA[CNY]]></fee_type>\n" +
                "  <is_subscribe><![CDATA[N]]></is_subscribe>\n" +
                "  <mch_id><![CDATA[1480435952]]></mch_id>\n" +
                "  <nonce_str><![CDATA[55dabe6751bb4203bd1a20d2bc2e0209]]></nonce_str>\n" +
                "  <openid><![CDATA[oZMHH05o05r6HQrMWtmdzjL9DfJs]]></openid>\n" +
                "  <out_trade_no><![CDATA[201807015712758472046313]]></out_trade_no>\n" +
                "  <result_code><![CDATA[SUCCESS]]></result_code>\n" +
                "  <return_code><![CDATA[SUCCESS]]></return_code>\n" +
                "  <sign><![CDATA[AD9306E2DE3F6894D4EBC6463CDBB8BC]]></sign>\n" +
                "  <sub_appid><![CDATA[wx4f61c3584dfea5f8]]></sub_appid>\n" +
                "  <sub_is_subscribe><![CDATA[Y]]></sub_is_subscribe>\n" +
                "  <sub_mch_id><![CDATA[1494838422]]></sub_mch_id>\n" +
                "  <sub_openid><![CDATA[oRhMkwxgxsynF1k8fZM4xfJcJv3w]]></sub_openid>\n" +
                "  <time_end><![CDATA[20180701161257]]></time_end>\n" +
                "  <total_fee>2050</total_fee>\n" +
                "  <trade_type><![CDATA[JSAPI]]></trade_type>\n" +
                "  <transaction_id><![CDATA[4200000142201807019918367246]]></transaction_id>\n" +
                "</xml>";

        String xml1 = "<xml><appid><![CDATA[wxed8e514891b578b9]]></appid><bank_type><![CDATA[CCB_CREDIT]]></bank_type><cash_fee><![CDATA[8455]]></cash_fee><fee_type><![CDATA[CNY]]></fee_type><is_subscribe><![CDATA[N]]></is_subscribe><mch_id><![CDATA[1480435952]]></mch_id><nonce_str><![CDATA[ccec43ccddee4726b59556789eef671e]]></nonce_str><openid><![CDATA[oZMHH05o05r6HQrMWtmdzjL9DfJs]]></openid><out_trade_no><![CDATA[201807015712758434028045]]></out_trade_no><result_code><![CDATA[SUCCESS]]></result_code><return_code><![CDATA[SUCCESS]]></return_code><sign><![CDATA[04471AA505012E274F26CA7F98E10BFC]]></sign><sub_appid><![CDATA[wx4f61c3584dfea5f8]]></sub_appid><sub_is_subscribe><![CDATA[Y]]></sub_is_subscribe><sub_mch_id><![CDATA[1494838422]]></sub_mch_id><sub_openid><![CDATA[oRhMkwxgxsynF1k8fZM4xfJcJv3w]]></sub_openid><time_end><![CDATA[20180701161202]]></time_end><total_fee>8455</total_fee><trade_type><![CDATA[JSAPI]]></trade_type><transaction_id><![CDATA[4200000156201807015560537456]]></transaction_id></xml>";
       WechatUnifiedorderNotifyRequest w=  (WechatUnifiedorderNotifyRequest) xmlToObject(WechatUnifiedorderNotifyRequest.class,xml);
        try {
            Map<String,String> map = xmlToMap(xml);
          /*  map.remove("sign");*/
            String temp = ObjectSortAscll.getSortMap(map);
            temp = temp.replaceAll("sign.+?&","");//取消sign 微信传回的sign不能参与签名
            temp +="&key="+ PropertiesLoader.getPropertiesValue("wechatKey");//使用配置文件
            logger.info("验证微信回调参数加key:"+temp);
           String sign = MD5Utils.parseStrToMd5U32(temp);
            System.out.println(sign);
            System.out.println(w.getSign());
            System.out.println(map.get("sign"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

  /*  public static void main(String[] args) {

        String xml = "<xml>" +
                "   <appid>wx2421b1c4370ec43b</appid>" +
                "   <sub_appid>wx2421b1c4370ec13b</sub_appid>" +
                "   <sub_mch_id>10000101</sub_mch_id>" +
                "   <attach>支付测试</attach>" +
                "   <body>JSAPI支付测试</body>" +
                "   <mch_id>10000100</mch_id>" +
                "   <nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>" +
                "   <notify_url>http://wxpay.wxutil.com/pub_v2/pay/notify.v2.php</notify_url>" +
                "   <openid>oUpF8uMuAJO_M2pxb1Q9zNjWeS6o</openid>" +
                "   <out_trade_no>1415659990</out_trade_no>" +
                "   <spbill_create_ip>14.23.150.211</spbill_create_ip>" +
                "   <total_fee>1</total_fee>" +
                "   <trade_type>JSAPI</trade_type>" +
                "   <sign>0CB01533B8C1EF103065174F50BCA001</sign>" +
                "</xml>";

        WechatUnifiedorderRequest request = new WechatUnifiedorderRequest();
        request.setAppid("123");

        WechatUnifiedorderRequest bean = (WechatUnifiedorderRequest) xmlToObject(WechatUnifiedorderRequest.class,xml);
        logger.info(bean.toString());
        logger.info("hello");
        logger.info(objectToXml(request));


        String responseXml = "<xml>" +
                "   <return_code><![CDATA[SUCCESS]]></return_code>" +
                "</xml>";
        logger.info("解析响应");
        WechatUnifiedorderResponse response = (WechatUnifiedorderResponse) xmlToObject(WechatUnifiedorderResponse.class,responseXml);
        logger.info(response.getReturn_code());
        logger.info("解析完毕");

        logger.info("xml转换对象开始");

        logger.info(objectToXml(response));
        logger.info("xml转换对象结束");
    }*/

    /**
     * 防注入XML，Document构造器
     * @return
     * @throws ParserConfigurationException
     */


}
